package simulador;

import java.util.*;
import smallsimulator.servicoInfo;

// Classe que representa um serviço com uma fila de espera associada

public class Servico {
	private int estado; // Variável que regista o estado do serviço: 0 - ocupado; >0 - numero de vagas
    private int atendidos; // Número de clientes atendidos até ao momento
	private double temp_ult, soma_temp_esp, soma_temp_serv, media_serv, desvio_serv; // Variáveis para cálculos estatísticos
        private int filaMax = 0; //máximo atingido pela fila neste serviço
	private Vector<Cliente> fila; // Fila de espera do serviço
	private Simulador s; // Referência para o simulador a que pertence o serviço
        private servicoInfo aInfo = new servicoInfo(); //objecto com a informação acerca dos parâmetros deste serviço
        private String nomeServico;
        private int esteId;
        private int tipoR = 0; //tipo de distribuição aleatória
        private Aleatorio randGen; //gerados aleatório para este serviço

	// Construtor
    Servico (Simulador s){
    	this.s = s;
		fila = new Vector <Cliente>(); // Cria fila de espera
		estado = 1; // Livre
		temp_ult = s.getInstante(); // Tempo que passou desde o último evento. Neste caso 0, porque a simulação ainda não começou.
		atendidos = 0;  // Inicialização de variáveis
		soma_temp_esp = 0;
		soma_temp_serv = 0;
                media_serv = 1.5;
                desvio_serv = 0;
                nomeServico = "Serviço Padrão";
                
                randGen = new Aleatorio(esteId);
	}
    
    Servico (Simulador s, servicoInfo info, int id){
        aInfo = info;
    	this.s = s;
		fila = new Vector <Cliente>(); // Cria fila de espera
		estado = info.getSlots(); // Livre
		temp_ult = s.getInstante(); // Tempo que passou desde o último evento. Neste caso 0, porque a simulação ainda não começou.
		atendidos = 0;  // Inicialização de variáveis
		soma_temp_esp = 0;
		soma_temp_serv = 0;
                media_serv = info.getTempoMedio();
                desvio_serv = info.getDesvio();
                nomeServico = info.getNome();
                esteId = id;
                if(info.getTipoRandom().equals("log"))
                    tipoR = -1;
                else if(info.getTipoRandom().equals("nor"))
                    tipoR = 1;
                else
                    tipoR = 0;
                
                randGen = new Aleatorio(esteId);
	}

	// Método que insere cliente (c) no serviço
    public void insereServico (Cliente c){
		if (estado > 0) { // Se serviço livre,
			estado --;     // fica ocupado e
			// agenda saída do cliente c para daqui a s.getMedia_serv() instantes
                        s.insereEvento (new Transito(sortNextTempo(s.getInstante()), s, esteId+1));
			//s.insereEvento (new Saida(s.getInstante()+media_serv, s));
                        //System.out.println(this.aInfo.getNome());
		}
		else fila.addElement(c); // Se serviço ocupado, o cliente vai para a fila de espera
	}

	// Método que remove cliente do serviço
    public void removeServico (){
		atendidos++; // Regista que acabou de atender + 1 cliente
		if (fila.size()== 0) estado = aInfo.getSlots(); // Se a fila está vazia, liberta o serviço
		else { // Se não,
		     // vai buscar próximo cliente à fila de espera e
			 // Cliente c = (Cliente)fila.firstElement();
			 fila.removeElementAt(0);
			 // agenda a sua saida para daqui a s.getMedia_serv() instantes
                         s.insereEvento (new Transito(sortNextTempo(s.getInstante()), s, esteId+1));
			 //s.insereEvento (new Saida(s.getInstante()+media_serv,s));
		}
	}
    
    //gera um tempo para o instante de fim deste serviço
    public double sortNextTempo(double instante){
        if(tipoR<0){
            return instante+randGen.exponencial(this.media_serv);
        }else if(tipoR>0){
            return instante+Math.abs(randGen.gaussian(media_serv, desvio_serv));
        }else {
            return instante+randGen.uniform(media_serv, desvio_serv);
        }
            
        
    }

	// Método que calcula valores para estatísticas, em cada passo da simulação ou evento
    public void act_stats(){
        // Calcula tempo que passou desde o último evento
		double temp_desde_ult = s.getInstante() - temp_ult;
		// Actualiza variável para o próximo passo/evento
		temp_ult = s.getInstante();
		// Contabiliza tempo de espera na fila
		// para todos os clientes que estiveram na fila durante o intervalo
                if(fila.size()>this.filaMax)
                    filaMax = fila.size();
		soma_temp_esp += fila.size() * temp_desde_ult;
		// Contabiliza tempo de atendimento
                double util = ((double)aInfo.getSlots()-(double)estado)/(double)aInfo.getSlots();
                //System.out.println("util: "+util);
		soma_temp_serv += util * temp_desde_ult;
	}

	// Método que calcula valores finais estatísticos
    public void relat (){
        // Tempo médio de espera na fila
		double temp_med_fila = soma_temp_esp / (atendidos+fila.size());
		// Comprimento médio da fila de espera
		// s.getInstante() neste momento é o valor do tempo de simulação,
        // uma vez que a simulação começou em 0 e este método só é chamdo no fim da simulação
		double comp_med_fila = soma_temp_esp / s.getInstante();
		// Tempo médio de atendimento no serviço
		double utilizacao_serv = soma_temp_serv / s.getInstante();
		// Apresenta resultados
                System.out.println("\n------"+this.nomeServico+"------");
                System.out.println("Tamanho máxima da fila "+this.filaMax);
		System.out.println("Tempo médio de espera "+temp_med_fila);
		System.out.println("Comp. médio da fila "+comp_med_fila);
		System.out.println("Utilização do serviço "+utilizacao_serv);
		System.out.println("Tempo de simulação "+s.getInstante()); // Valor actual
		System.out.println("Número de clientes atendidos "+atendidos);
		System.out.println("Número de clientes na fila "+fila.size()); // Valor actual
                //System.out.println(this.aInfo.toString());
	}
    
    public String getRelatFinal (){
        // Tempo médio de espera na fila
                String retorno = "";
		double temp_med_fila = soma_temp_esp / (atendidos+fila.size());
		// Comprimento médio da fila de espera
		// s.getInstante() neste momento é o valor do tempo de simulação,
        // uma vez que a simulação começou em 0 e este método só é chamdo no fim da simulação
		double comp_med_fila = soma_temp_esp / s.getInstante();
		// Tempo médio de atendimento no serviço
		double utilizacao_serv = soma_temp_serv / s.getInstante();
		// Apresenta resultados
                retorno += "\n------"+this.nomeServico+"------"+"\n";
                retorno += "Tamanho máxima da fila "+this.filaMax+"\n";
		retorno += "Tempo médio de espera "+temp_med_fila+"\n";
		retorno += "Comp. médio da fila "+comp_med_fila+"\n";
		retorno += "Utilização do serviço "+utilizacao_serv+"\n";
		retorno += "Tempo de simulação "+s.getInstante()+"\n"; // Valor actual
		retorno += "Número de clientes atendidos "+atendidos+"\n";
		retorno += "Número de clientes na fila "+fila.size()+"\n"; // Valor actual
                //retorno += this.aInfo.toString()+"\n";
                return retorno;
	}

    // Método que devolve o número de clientes atendidos no serviço até ao momento
    public int getAtendidos() {
        return atendidos;
    }
    
    public int getFilaMax(){
        return this.filaMax;
    }
    public double getTempoMed(){
        double temp_med_fila = soma_temp_esp / (atendidos+fila.size());
        return temp_med_fila;
    }
    public double getCompMed(){
        double comp_med_fila = soma_temp_esp / s.getInstante();
        return comp_med_fila;
    }
    public double getUtil(){
        double utilizacao_serv = soma_temp_serv / s.getInstante();
        return utilizacao_serv;
    }
    public double getFilaS(){
        return this.fila.size();
    }

}